/*
 * Copyright ©2015-2021 Jaemon. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.gitee.jaemon.spring.adorner.core;

import io.gitee.jaemon.spring.adorner.annotation.ApiIgnore;
import io.gitee.jaemon.spring.adorner.annotation.ApiOrder;
import io.gitee.jaemon.spring.adorner.exception.AdornerException;
import io.gitee.jaemon.spring.adorner.support.AdornerNoteScanner;
import io.gitee.jaemon.spring.adorner.type.AnnotationPair;
import io.gitee.jaemon.spring.adorner.type.ApiDefinition;
import io.gitee.jaemon.spring.adorner.type.SortPair;
import io.gitee.jaemon.spring.adorner.util.AdornerUtils;
import io.gitee.jaemon.spring.adorner.util.ClassUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.stream.Collectors;

import static io.gitee.jaemon.spring.adorner.core.DocContainer.CONTAINER;
import static io.gitee.jaemon.spring.adorner.util.LoggerHelper.*;

/**
 * 执行器模板
 *
 * @author Jaemon
 * @since 1.0
 */
public abstract class AbstractExecutor<T>
        extends ApiDefinitionResolver<T>
        implements Executor {
    /**
     * Adorner执行器上下文
     */
    protected final AdornerApplicationContext context;
    /**
     * 文档生成器
     */
    protected final DocGenerator<T> docGenerator;
    /**
     * 注释扫描器
     */
    protected final NoteScanner noteScanner;

    public AbstractExecutor(
            AdornerApplicationContext context,
            ParamHandler<T> paramHandler,
            DocGenerator<T> docGenerator
    ) {
        this(context, paramHandler, docGenerator, new AdornerNoteScanner());
    }

    public AbstractExecutor(
            AdornerApplicationContext context,
            ParamHandler<T> paramHandler,
            DocGenerator<T> docGenerator,
            NoteScanner noteScanner)
    {
        super(paramHandler);
        this.context = context;
        this.docGenerator = docGenerator;
        this.noteScanner = noteScanner;

        AdornerLogConfig.loadLogConfig(context);
        debug(context.show());
        AdornerProperty.INSTANCE.init();
    }

    @Override
    public void execute() {
        try {
            File file = new File(context.filePath());
            if (file.exists()) {
                warn("文件[%s]已经存在, 请先备份或清理后再试.", context.filePath());
                return;
            }

            if (noteScanner.doScan(context)) {
                info("扫描注释完成, 容器信息=[%s]", CONTAINER.message());
            }

            List<Class<?>> controllers = doScan();
            if (controllers.isEmpty()) {
                info("扫描路径%s下接口类为空.", Arrays.asList(context.basePackages()));
                return;
            }
            info("扫描完成, 本次共扫描到%d个接口类", controllers.size());

            List<ApiDefinition<T>> apiDefinitions = convert(controllers);
            if (apiDefinitions.isEmpty()) {
                info("包路径%s下定义的api接口为空.", Arrays.asList(context.basePackages()));
                return;
            }
            info("解析完成, 本次共解析到%d个api接口", apiDefinitions.size());

            generate(apiDefinitions);
            info("文档生成完成, 生成路径=[%s]", context.filePath());
        } catch (AdornerException ex) {
            error("AdornerException: %s", ex.getMessage());
        }
    }


    /**
     * 扫描器
     *
     * @return
     *      控制层类
     * @throws AdornerException
     *      ex
     */
    public List<Class<?>> doScan() throws AdornerException {
        List<Class<?>> controllers = new ArrayList<>();
        ClassUtils.doScan(controllers, Controller.class, context.basePackages());
        return controllers;
    }

    /**
     * 转换
     *
     * @param controllers
     *      控制层类
     * @return
     *      api定义对象集 {@link ApiDefinition}
     * @throws AdornerException
     *      ex
     */
    public List<ApiDefinition<T>> convert(List<Class<?>> controllers) throws AdornerException {
        List<ApiDefinition<T>> apiDefinitions = new ArrayList<>();

        List<SortPair<Class<?>>> validControllers = validControllers(controllers);

        // 遍历所有接口类
        for (int i = 0; i < validControllers.size(); i++) {
            SortPair<Class<?>> classSortPair = validControllers.get(i);
            Class<?> controller = classSortPair.getElement();

            debug("Identified candidate component class: file[%s%s.class]",
                    controller.getResource("").getPath(), controller.getSimpleName());

            // 获取所有接口
            List<SortPair<Method>> declaredMethods = validMethods(controller);

            for (int j = 0; j < declaredMethods.size(); j++) {
                SortPair<Method> methodSortPair = declaredMethods.get(j);
                Method declaredMethod = methodSortPair.getElement();
                int apiOrder = methodSortPair.getOrder();

                // 定义了RequestMapping注解的方法才有效
                AnnotationPair<RequestMapping> annotationPair = AdornerUtils.hasAnnotation(declaredMethod, RequestMapping.class);
                if (declaredMethod.getModifiers() != Modifier.PUBLIC || annotationPair == null) {
                    debug("Skip method[%s.%s()] a because it's not decorated by public or not decorated with @RequestMapping.",
                            controller.getName(), declaredMethod.getName());
                    continue;
                }

                apiDefinitions.add(
                        parse(
                                controller,
                                ClassUtils.urlFormat(context.url(), false),
                                context.group() ? i * 1000 + j : apiOrder,
                                declaredMethod,
                                annotationPair
                        )
                );
                debug("Identified candidate API interfaces [%s.%s()]",
                        controller.getName(), declaredMethod.getName());
            }
        }
        return apiDefinitions.stream()
                .sorted(
                        Comparator.comparingInt(ApiDefinition::getOrder)
                )
                .collect(Collectors.toList());
    }

    /**
     * 生成文档
     *
     * @param apiDefinitions
     *      api定义对象集 {@link ApiDefinition}
     * @throws AdornerException
     *      ex
     */
    public void generate(List<ApiDefinition<T>> apiDefinitions) throws AdornerException {
        docGenerator.generate(apiDefinitions, context);
    }


    /**
     * 获取有效的接口类
     *
     * @param controllers
     *          接口类集合
     * @return
     *          接口类集
     */
    protected List<SortPair<Class<?>>> validControllers(List<Class<?>> controllers) {
        List<SortPair<Class<?>>> result = new ArrayList<>(controllers.size());

        for (Class<?> controller : controllers) {
            List<Class<?>> includeClasses = context.includeClasses();
            if (includeClasses.isEmpty()) {
                if (context.excludeClasses().contains(controller)) {
                    debug("Skip class[%s] a because it's in the exclusion class",
                            controller.getName());
                    continue;
                }
            } else {
                if (!includeClasses.contains(controller)) {
                    debug("Skip class[%s] a because it is not in the inclusion class.",
                            controller.getName());
                    continue;
                }
            }

            if (
                    controller.isAnnotationPresent(ApiIgnore.class) &&
                            controller.getAnnotation(ApiIgnore.class).value()
            ) {
                debug("Skip class[%s] a because it is decorated with @ApiIgnore annotation with value true.",
                        controller.getName());
                continue;
            }

            int order = controller.isAnnotationPresent(ApiOrder.class) ?
                    controller.getAnnotation(ApiOrder.class).value() : ApiOrder.LOWEST_PRECEDENCE;
            result.add(new SortPair<>(controller, order));
        }

        return result.stream()
                .sorted(
                        Comparator.comparingInt(SortPair::getOrder)
                )
                .collect(Collectors.toList());
    }

    /**
     * 获取接口类中有效的接口
     *
     * @param controller
     *          接口类
     * @return
     *          接口集合
     */
    protected List<SortPair<Method>> validMethods(Class<?> controller) {
        Method[] methods = controller.getDeclaredMethods();
        List<SortPair<Method>> result = new ArrayList<>(methods.length);

        for (Method method : methods) {
            if (
                    method.isAnnotationPresent(ApiIgnore.class) &&
                            method.getAnnotation(ApiIgnore.class).value()
            ) {
                debug("Skip method[%s.%s()] a because it is decorated with @ApiIgnore annotation with value true.",
                        controller.getName(), method.getName());
                continue;
            }

            int order = method.isAnnotationPresent(ApiOrder.class) ?
                    method.getAnnotation(ApiOrder.class).value() : ApiOrder.LOWEST_PRECEDENCE;
            result.add(new SortPair<>(method, order));
        }


        return result.stream()
                .sorted(
                        Comparator.comparingInt(SortPair::getOrder)
                )
                .collect(Collectors.toList());
    }
}